package com.platform.comm.common.batch;

import com.google.common.collect.Lists;
import com.platform.batch.base.component.reader.ExcelStreamingItemReader;
import com.platform.batch.base.exception.JobExecuteFailedException;
import com.platform.batch.base.exception.JobNeedSkippedException;
import com.platform.batch.base.exception.JobSkippedWithLimitException;
import com.platform.comm.common.batch.listener.*;
import com.platform.comm.common.batch.processor.ComImportDataDecryptProcessor;
import com.platform.comm.common.batch.processor.ComImportDataTranslateProcessor;
import com.platform.comm.common.batch.processor.ComImportDataValidationProcessor;
import com.platform.comm.common.batch.processor.ImportDataRepeatProcessor;
import com.platform.comm.common.batch.writer.SysImpItemWriter;
import com.platform.comm.exceptions.ValidationException;
import com.platform.comm.util.ProjectUtils;
import com.platform.vo.ExcelColCfgDto;
import com.platform.vo.ImportDto;
import com.platform.comm.common.batch.listener.*;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.JobScope;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.support.CompositeItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.FileSystemResource;
import org.springframework.retry.policy.NeverRetryPolicy;

import javax.annotation.Nullable;
import java.util.List;

/**
 * Description: 通用读取数据配置
 *
 * @author lx Created on 2020/01/06
 **/
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
@Configuration
public class ComBatchConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job comBatchJob() {
        return jobBuilderFactory.get("comImportJob")
                .incrementer(new RunIdIncrementer())
                .start(comBatchStep()).listener(jobImportExecutionListener(null))
                .build();
    }

    @Bean
    public Step comBatchStep() {
        return stepBuilderFactory.get("comImportStep")
                .listener(importDataCountStepExecutionListener(null))
                .<ImportDto, ImportDto>chunk(1000)
                .reader(comItemReader(null))
                .processor(compositeItemProcessor())
                .listener(importDataCountProcessListener(null))
                .writer(sysImpWriter(null))
                .faultTolerant().skipLimit(100).skip(JobSkippedWithLimitException.class)
                .skip(JobNeedSkippedException.class)
                .noSkip(JobExecuteFailedException.class)
                .retryPolicy(new NeverRetryPolicy())
                .listener(new CustomSkipListener())
                .listener(new CustomChunkListener())
                .build();
    }

    @Bean
    @StepScope
    public ExcelStreamingItemReader<ImportDto> comItemReader(@Nullable
                                                             @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        assert batchContext != null;
        // 文件路径
        String filePath = ProjectUtils.getImportFilePath("sys", batchContext.getCommDataInfoEntity().getFileUrl());
        ExcelStreamingItemReader<ImportDto> reader = new ExcelStreamingItemReader<>();
        reader.setResource(new FileSystemResource(filePath));
        reader.setLinesToSkip(2);
        reader.setSkippedLinesCallback(line -> {
            String[] header = line.split(",");
            List<ExcelColCfgDto> colConfigs = batchContext.getImportConfig().getColumns();
            for (int i = 0; i < colConfigs.size(); i++) {
                if (!"CHECK_ERR_MSG".equals(colConfigs.get(i).getFieldName()) && !colConfigs.get(i).getTitle().equals(header[i])) {
                    throw new ValidationException("模板格式不正确,请下载最新导入模板");
                }
            }
        });
        reader.setConvert((map) -> {
            ImportDto importDto = new ImportDto();
            List<ExcelColCfgDto> columns = batchContext.getImportConfig().getColumns();
            columns.forEach(column -> importDto.setData(column.getFieldName(), map.get(column.getTitle())));

            return importDto;
        });

        return reader;
    }

    @Bean("comCompositeItemProcessor")
    @Scope("prototype")
    public CompositeItemProcessor<ImportDto, ImportDto> compositeItemProcessor() {
        CompositeItemProcessor<ImportDto, ImportDto> compositeItemProcessor = new CompositeItemProcessor<>();
        List<ItemProcessor<ImportDto, ImportDto>> list = Lists.newArrayList();
        list.add(importDataTranslateProcessor(null));
        list.add(importDataValidationProcessor(null));
        list.add(new ComImportDataDecryptProcessor());
        list.add(comImportDataRepeatProcessor(null));

        compositeItemProcessor.setDelegates(list);

        return compositeItemProcessor;
    }

    @Bean("comImportDataTranslateProcessor")
    @StepScope
    public ItemProcessor<ImportDto, ImportDto> importDataTranslateProcessor(
            @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        return new ComImportDataTranslateProcessor(batchContext);
    }

    @Bean("comImportDataValidationProcessor")
    @StepScope
    public ItemProcessor<ImportDto, ImportDto> importDataValidationProcessor(
            @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        return new ComImportDataValidationProcessor(batchContext);
    }

    @Bean
    @StepScope
    public ItemProcessor<ImportDto, ImportDto> comImportDataRepeatProcessor(
            @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        return new ImportDataRepeatProcessor(batchContext);
    }

    @Bean("sysImpWriter")
    @StepScope
    public SysImpItemWriter sysImpWriter(@Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        return new SysImpItemWriter(batchContext);
    }

    @Bean("comImportDataCountProcessListener")
    @StepScope
    public ImportDataProcessCountListener importDataCountProcessListener(@Nullable
                                                                         @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        assert batchContext != null;
        return new ImportDataProcessCountListener(batchContext);
    }

    @Bean("comImportDataCountStepListener")
    @StepScope
    public ImportDataCountStepExecutionListener importDataCountStepExecutionListener(@Nullable
                                                                                     @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        assert batchContext != null;
        return new ImportDataCountStepExecutionListener(batchContext);
    }

    @Bean("comReadJobExecutionListener")
    @JobScope
    public JobExecutionListener jobImportExecutionListener(
            @Value("#{jobExecutionContext['config']}") ComBatchContext batchContext) {
        return new ComImportJobListener(batchContext);
    }

}
